#include <iostream>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <thread>

#include "luaManager.h"
#include "crc32.h"
#include <lua5.4/lua.hpp>

using namespace std;
// 用来连接服务端，并测试接收和发送消息

int sockfd;// 定义为全局

// 包头结构
#pragma pack (1)// 先设置1字节对齐，保证包头结构体紧密排列
// 包头结构体
typedef struct _COMM_PKG_HEADER{
    unsigned short pkgLen;// 报文总长度（2字节）包头+包体
    unsigned short msgCode;// 消息类型（2字节），也就是需要调用的函数
    int crc32;// CRC32校验码（4字节）
}COMM_PKG_HEADER,*LPCOMM_PKG_HEADER;
#pragma pack() // 再恢复默认对齐，避免影响其他结构体

// 暴露给lua层的函数声明
int _Test(lua_State* L);
int _GetFd(lua_State* L);
int Send2Server(std::string body, size_t len);

// 编译命令 g++ main.cpp luaManager.cpp -o client -llua5.4
// GDB编译 g++ -g -O0 main.cpp luaManager.cpp -o client -llua5.4

int main(int argc, char const *argv[])
{
    // 1.连接服务端
    const char* ip = "127.0.0.1";
    const int port = 11111;
    // 创建socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);// 获取TCP套接字
    if(sockfd == -1) {
        std::cerr << "客户端无法创建套接字: " << strerror(errno) << std::endl;
        return -1;
    }
    // 设置服务器地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    if(inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0){
        std::cerr << "无效的ip地址: " << ip << std::endl;
        close(sockfd);
        return -1;
    }
    // 连接服务器
    if(connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
        std::cerr << "客户端尝试连接失败: " << strerror(errno) << std::endl;
        close(sockfd);
        return -1;
    }
    std::cout << "客户端成功连接" << ip << ":" << port << std::endl;

    // 2.启动lua脚本并暴露C++函数
    LuaManager luaMgr;// 创建一个管理
    luaMgr.PutNewExposeFunc("_Test", _Test);
    luaMgr.PutNewExposeFunc("_GetFd", _GetFd);
    if(!luaMgr.Initialize()){
        std::cout << "初始化脚本管理器失败" << std::endl;
        close(sockfd);
        return -1;
    }

    // 3.在lua层打包数据发给C++层
    std::cout << "客户端发送成功, 进入死循环..." << std::endl;
    while(true){
        this_thread::sleep_for(chrono::milliseconds(500));// 适当休眠，避免忙等待
    }
    
    close(sockfd);
    return 0;
}

// 实现（将lua层发来的数据通过网络发送给业务进程）
int _Test(lua_State* L){
    size_t len = 0;
    const char* str = luaL_checklstring(L, 1, &len);

    // 发送到服务端
    errno = 0;
    if(sockfd == -1){
        lua_pushboolean(L, false);
        return 1;
    }
    
    // 调用打包函数，加入包头信息后发送给客户端
    if(Send2Server(str, len) == -1){
        lua_pushboolean(L, false);
        return 1;
    }

    lua_pushboolean(L, true);
    return 1;
}

int _GetFd(lua_State* L){
    if(sockfd == -1){
        lua_pushboolean(L, false);
        return 1;
    }
    lua_pushinteger(L, sockfd);
    return 1;
}

int Send2Server(std::string body, size_t len){
    // 1. 参数校验
    if (sockfd == -1) {
        std::cerr << "ERROR: 无效的socket描述符" << std::endl;
        return -1;
    }
    if (len == 0) {
        std::cerr << "WARN: 尝试发送空包体" << std::endl;
        return -1;
    }

    // 2.创建包头结构体并填充
    size_t headLen = sizeof(COMM_PKG_HEADER);
    const size_t total_len = headLen + len;
    if (total_len > 30000) { // 限制最大30000字节
        std::cerr << "ERROR: 数据包过大(" << total_len << "字节)" << std::endl;
        return -1;
    }

    // 3. 分配内存（使用智能指针避免泄漏）
    std::unique_ptr<char[]> buff(new char[total_len]);
    if (!buff) {
        std::cerr << "ERROR: 内存分配失败(" << total_len << "字节)" << std::endl;
        return -1;
    }

    // 4. 填充包头
    LPCOMM_PKG_HEADER header = reinterpret_cast<LPCOMM_PKG_HEADER>(buff.get());
    header->pkgLen = htons(static_cast<unsigned short>(total_len)); // 注意类型转换
    header->msgCode = htons(5); // 示例消息类型

    // 5. 计算CRC32校验
    CCRC32* m_crc = CCRC32::GetInstance();
    std::vector<unsigned char> tmp_buf(body.begin(), body.end());
    int calccrc = m_crc->Get_CRC(tmp_buf.data(), tmp_buf.size());
    header->crc32 = htonl(calccrc);

    // 6. 拷贝包体数据
    memcpy(buff.get() + headLen, body.data(), len);

    return send(sockfd, buff.get(), total_len, MSG_NOSIGNAL);
}